%include "boot.inc"

section code vstart=LOADER_BASE_ADDR
	jmp loader_start

times 0x10 - ($ - $$) db 0

; current address is LOADER_BASE_ADDR + 0x10
total_mem_bytes dd 0
dd 0

GDT_BASE:
	dd 0x0
	dd 0x0
GDT_CODE_BASE:
	dd 0x0000ffff
	dd 0000_0000_1101_1111_1001_1000_0000_0000b
GDT_DATA_BASE:
	dd 0x0000ffff
	dd 0000_0000_1101_1111_1001_0010_0000_0000b
GDT_VIDEO_BASE:
	dd 0x80000007
	dd 0000_0000_1101_1111_1001_0010_0000_1011b
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0

GDTR:
	dw GDT_LIMIT
	dd GDT_BASE
CODE_SELECTOR equ 0x0008
DATA_SELECTOR equ 0x0010
VIDEO_SELECTOR equ 0x0018

ards_buf times 244 db 0
ards_nr dw 0
; =============================================================
loader_start:
; get memery size
	;bios int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP')
	xor ebx, ebx
	mov edx, 0x534d4150
	mov di, ards_buf
.e820_mem_get_loop:
	mov eax, 0x0000e820
	mov ecx, 20
	int 0x15
	jc .e820_failed
	add di, cx
	inc word [ards_nr]
	cmp ebx, 0
	jnz .e820_mem_get_loop

	mov cx, [ards_nr]
	mov ebx, ards_buf
	xor edx, edx
.find_max_mem_area:
	mov eax, [ebx]
	add eax, [ebx+8]
	add ebx, 20
	cmp edx, eax
	jge .next_ards
	mov edx, eax
.next_ards:
	loop .find_max_mem_area
	jmp .mem_get_ok

.e820_failed:
	mov edx, 32 * 1024 * 1024
.mem_get_ok:
	mov [total_mem_bytes], edx

	; set cursor
	mov ah, 0x02
	mov bh, 0x0
	mov dh, 0x2
	mov dl, 0x0
	int 0x10
	
	; start protect mode
	; A20
	in ax, 0x92
	or ax, 0x02
	out 0x92, ax

	; GDT register
	lgdt [GDTR]

	; CR0 open PE bit
	mov eax, cr0
	or eax, 0x00000001
	mov cr0, eax

	; update cs
	jmp CODE_SELECTOR:protect_mode_32

[bits 32]
protect_mode_32:
	; set segment register
	mov ax, DATA_SELECTOR
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov ss, ax
	mov esp, LOADER_STACK_BASE_ADDR
	mov ax, VIDEO_SELECTOR
	mov gs, ax

	; set page table
	call setup_page_table

	or esp, 0xc0000000
	or dword [GDT_VIDEO_BASE + 4], 0xc0000000
	or dword [GDTR + 2], 0xc0000000

	; set CR3
	mov eax, PDE_BASE_ADDR
	mov cr3, eax

	; open CR0 PG bit
	mov eax, cr0
	or eax, 0x80000000
	mov cr0, eax

	lgdt [GDTR]

	; start to use virtual address

	; mov byte [gs:0xA0], "V"
	; mov byte [gs:0xA1], 0x07
	
	; load kernel.bin
	mov eax, KERNEL_START_SECTOR
	mov ebx, KERNEL_BIN_BASE_ADDR
	mov ecx, KERNEL_SECTOR_COUNT
	call read_disk_32
	
	call kernel_init
	
	cmp eax, 0
	jnz kernel_init_error
	
	mov esp, KERNEL_STACK_BASE_ADDR

	mov byte [gs:0xA0], "O"
	mov byte [gs:0xA1], 0x07
	mov byte [gs:0xA2], "K"
	mov byte [gs:0xA3], 0x07

	jmp KERNEL_ENTRY_ADDR
	
kernel_init_error:
	mov byte [gs:0xA0], "E"
	mov byte [gs:0xA1], 0x07
	jmp $
; =============================================================
; =============================================================
; setup page table in PDE_BASE_ADDR
setup_page_table:
	mov eax, PDE_BASE_ADDR
	mov ebx, 0x0
	mov ecx, 4096 + 4096 * (256 - 1) ; PDE + PTE * (1G / 4M - 1)
	call memset

	mov eax, PDE_BASE_ADDR + 0x1000 + PG_P + PG_RW_W + PG_US_U
	mov [PDE_BASE_ADDR], eax
	mov [PDE_BASE_ADDR + 768 * 4], eax

	mov ecx, 254
	mov esi, 768
.init_high_1G_PDE:
	add eax, 0x1000
	inc esi
	mov [PDE_BASE_ADDR + esi * 4], eax
	loop .init_high_1G_PDE

	; init low 1M PTE
	mov ecx, 256 ; 1M / 4K
	mov eax, PG_P + PG_RW_W + PG_US_U
	mov ebx, PDE_BASE_ADDR + 0x1000
	mov esi, 0
.init_low_1M_PTE:
	mov [ebx + esi * 4], eax
	add eax, 0x1000
	inc esi
	loop .init_low_1M_PTE

	; set the last one PDE
	mov eax, PDE_BASE_ADDR + PG_P + PG_RW_W + PG_US_U
	mov [PDE_BASE_ADDR + 1023 * 4], eax

	ret
; =============================================================
; =============================================================
kernel_init:
	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx
	xor edx, edx
	xor esi, esi
	xor edi, edi
	
	; check magic num
	cmp dword [KERNEL_BIN_HEADER], 0x464c457f
	jnz kernel_init_faild
	
	mov ebx, [KERNEL_BIN_HEADER + 28] ; e_phoff
	mov dx, [KERNEL_BIN_HEADER + 42] ; e_phentsize
	mov cx, [KERNEL_BIN_HEADER + 44] ; e_phnum

	add ebx, KERNEL_BIN_HEADER
.cpy_program_header:
	cmp dword [ebx], PT_NULL
	jz .cpy_program_header_next
	
	mov esi, [ebx + 4] ; p_offset
	add esi, KERNEL_BIN_HEADER
	mov edi, [ebx + 8] ; p_vaddr
	push ecx
	mov ecx, [ebx + 16] ; p_filesz
	rep movsb
	pop ecx
	
.cpy_program_header_next:
	add ebx, edx
	loop .cpy_program_header
	
kernel_init_success:
	mov eax, 0
	ret
kernel_init_faild:
	mov eax, -1
	ret

; =============================================================
; read disk sector in 32 bit protect mode
; eax: LBA secctor
; ebx: dst addr
; ecx: sector count
read_disk_32:
	mov esi, eax
	mov edi, ecx

	; 1. sector count
	mov al, cl
	mov dx, 0x1f2
	out dx, al
	
	; 2. LBA
	mov eax, esi
	mov cl, 0x8

	; bit0-bit7
	mov dx, 0x1f3
	out dx, al

	; bit8-bit15
	mov dx, 0x1f4
	shr eax, cl
	out dx, al

	; bit16-bit23
	mov dx, 0x1f5
	shr eax, cl
	out dx, al

	; bit24-bit27
	mov dx, 0x1f6
	shr eax, cl
	and al, 0x0f
	or al, 0xe0	; device bit4-bit7
	out dx, al

	; 3. read command
	mov dx, 0x1f7
	mov al, 0x20
	out dx, al

	; 4. wait for readable
	mov dx, 0x1f7
.wait_for_readable:
	in al, dx
	and al,0x88
	cmp al, 0x8
	jnz .wait_for_readable

	; 5. read data
	mov eax, 256
	mul edi
	mov ecx, eax
	mov dx, 0x1f0
.read_data:
	in ax, dx
	mov word [ebx], ax
	add ebx, 2
	loop .read_data

	ret
; =============================================================
; =============================================================
; memset
; eax: start address
; ebx: value
; ecx: byte
memset:
	mov [eax], ebx
	inc eax
	loop memset
	ret
; =============================================================

